home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / FLTK-1.0.6 / src / Fl_Browser.cxx < prev    next >
Encoding:
C/C++ Source or Header  |  1999-05-11  |  12.7 KB  |  493 lines

  1. //
  2. // "$Id: Fl_Browser.cxx,v 1.9.2.6 1999/05/11 09:39:29 bill Exp $"
  3. //
  4. // Browser widget for the Fast Light Tool Kit (FLTK).
  5. //
  6. // Copyright 1998-1999 by Bill Spitzak and others.
  7. //
  8. // This library is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU Library General Public
  10. // License as published by the Free Software Foundation; either
  11. // version 2 of the License, or (at your option) any later version.
  12. //
  13. // This library is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. // Library General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Library General Public
  19. // License along with this library; if not, write to the Free Software
  20. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  21. // USA.
  22. //
  23. // Please report all bugs and problems to "fltk-bugs@easysw.com".
  24. //
  25.  
  26. #include <FL/Fl.H>
  27. #include <FL/Fl_Browser.H>
  28. #include <FL/fl_draw.H>
  29. #include <string.h>
  30. #include <stdlib.h>
  31. #include <math.h>
  32.  
  33. // I modified this from the original Forms data to use a linked list
  34. // so that the number of items in the browser and size of those items
  35. // is unlimited.  The only problem is that the old browser used an
  36. // index number to identify a line, and it is slow to convert from/to
  37. // a pointer.  I use a cache of the last match to try to speed this
  38. // up.
  39.  
  40. // Also added the ability to "hide" a line.  This set's it's height to
  41. // zero, so the Fl_Browser_ cannot pick it.
  42.  
  43. #define SELECTED 1
  44. #define NOTDISPLAYED 2
  45.  
  46. struct FL_BLINE {    // data is in a linked list of these
  47.   FL_BLINE* prev;
  48.   FL_BLINE* next;
  49.   void* data;
  50.   short length;        // sizeof(txt)-1, may be longer than string
  51.   char flags;        // selected, displayed
  52.   char txt[1];        // start of allocated array
  53. };
  54.  
  55. void* Fl_Browser::item_first() const {return first;}
  56.  
  57. void* Fl_Browser::item_next(void* l) const {return ((FL_BLINE*)l)->next;}
  58.  
  59. void* Fl_Browser::item_prev(void* l) const {return ((FL_BLINE*)l)->prev;}
  60.  
  61. int Fl_Browser::item_selected(void* l) const {
  62.   return ((FL_BLINE*)l)->flags&SELECTED;}
  63.  
  64. void Fl_Browser::item_select(void* l, int v) {
  65.   if (v) ((FL_BLINE*)l)->flags |= SELECTED;
  66.   else ((FL_BLINE*)l)->flags &= ~SELECTED;
  67. }
  68.  
  69. FL_BLINE* Fl_Browser::find_line(int line) const {
  70.   int n; FL_BLINE* l;
  71.   if (line == cacheline) return cache;
  72.   if (cacheline && line > (cacheline/2) && line < ((cacheline+lines)/2)) {
  73.     n = cacheline; l = cache;
  74.   } else if (line <= (lines/2)) {
  75.     n = 1; l = first;
  76.   } else {
  77.     n = lines; l = last;
  78.   }
  79.   for (; n < line && l; n++) l = l->next;
  80.   for (; n > line && l; n--) l = l->prev;
  81.   ((Fl_Browser*)this)->cacheline = line;
  82.   ((Fl_Browser*)this)->cache = l;
  83.   return l;
  84. }
  85.  
  86. int Fl_Browser::lineno(void* v) const {
  87.   FL_BLINE* l = (FL_BLINE*)v;
  88.   if (!l) return 0;
  89.   if (l == cache) return cacheline;
  90.   if (l == first) return 1;
  91.   if (l == last) return lines;
  92.   if (!cache) {
  93.     ((Fl_Browser*)this)->cache = first;
  94.     ((Fl_Browser*)this)->cacheline = 1;
  95.   }
  96.   // assumme it is near cache, search both directions:
  97.   FL_BLINE* b = cache->prev;
  98.   int bnum = cacheline-1;
  99.   FL_BLINE* f = cache->next;
  100.   int fnum = cacheline+1;
  101.   int n = 0;
  102.   for (;;) {
  103.     if (b == l) {n = bnum; break;}
  104.     if (f == l) {n = fnum; break;}
  105.     if (b) {b = b->prev; bnum--;}
  106.     if (f) {f = f->next; fnum++;}
  107.   }
  108.   ((Fl_Browser*)this)->cache = l;
  109.   ((Fl_Browser*)this)->cacheline = n;
  110.   return n;
  111. }
  112.  
  113. FL_BLINE* Fl_Browser::_remove(int line) {
  114.   FL_BLINE* ttt = find_line(line);
  115.   deleting(ttt);
  116.  
  117.   cacheline = line-1;
  118.   cache = ttt->prev;
  119.   if (ttt->prev) ttt->prev->next = ttt->next;
  120.   else first = ttt->next;
  121.   if (ttt->next) ttt->next->prev = ttt->prev;
  122.   else last = ttt->prev;
  123.  
  124.   lines--;
  125.   full_height_ -= item_height(ttt);
  126.   return(ttt);
  127. }
  128.  
  129. void Fl_Browser::remove(int line) {
  130.   if (line < 1 || line > lines) return;
  131.   free(_remove(line));
  132. }
  133.  
  134. void Fl_Browser::insert(int line, FL_BLINE* t) {
  135.   if (!first) {
  136.     t->prev = t->next = 0;
  137.     first = last = t;
  138.   } else if (line <= 1) {
  139.     inserting(first, t);
  140.     t->prev = 0;
  141.     t->next = first;
  142.     t->next->prev = t;
  143.     first = t;
  144.   } else if (line > lines) {
  145.     t->prev = last;
  146.     t->prev->next = t;
  147.     t->next = 0;
  148.     last = t;
  149.   } else {
  150.     FL_BLINE* n = find_line(line);
  151.     inserting(n, t);
  152.     t->next = n;
  153.     t->prev = n->prev;
  154.     t->prev->next = t;
  155.     n->prev = t;
  156.   }
  157.   cacheline = line;
  158.   cache = t;
  159.   lines++;
  160.   full_height_ += item_height(t);
  161.   redraw_line(t);
  162. }
  163.  
  164. void Fl_Browser::insert(int line, const char* newtext, void* data) {
  165.   int l = strlen(newtext);
  166.   FL_BLINE* t = (FL_BLINE*)malloc(sizeof(FL_BLINE)+l);
  167.   t->length = l;
  168.   t->flags = 0;
  169.   strcpy(t->txt, newtext);
  170.   t->data = data;
  171.   insert(line, t);
  172. }
  173.  
  174. void Fl_Browser::move(int to, int from) {
  175.   if (from < 1 || from > lines) return;
  176.   insert(to, _remove(from));
  177. }
  178.  
  179. void Fl_Browser::text(int line, const char* newtext) {
  180.   if (line < 1 || line > lines) return;
  181.   FL_BLINE* t = find_line(line);
  182.   int l = strlen(newtext);
  183.   if (l > t->length) {
  184.     FL_BLINE* n = (FL_BLINE*)malloc(sizeof(FL_BLINE)+l);
  185.     replacing(t, n);
  186.     cache = n;
  187.     n->data = t->data;
  188.     n->length = l;
  189.     n->flags = t->flags;
  190.     n->prev = t->prev;
  191.     if (n->prev) n->prev->next = n; else first = n;
  192.     n->next = t->next;
  193.     if (n->next) n->next->prev = n; else last = n;
  194.     free(t);
  195.     t = n;
  196.   }
  197.   strcpy(t->txt, newtext);
  198.   redraw_line(t);
  199. }
  200.  
  201. void Fl_Browser::data(int line, void* data) {
  202.   if (line < 1 || line > lines) return;
  203.   find_line(line)->data = data;
  204. }
  205.  
  206. int Fl_Browser::item_height(void* lv) const {
  207.   FL_BLINE* l = (FL_BLINE*)lv;
  208.   if (l->flags & NOTDISPLAYED) return 0;
  209.  
  210.   int hmax = 2; // use 2 to insure we don't return a zero!
  211.  
  212.   if (!l->txt[0]) {
  213.     // For blank lines set the height to exactly 1 line!
  214.     fl_font(textfont(), textsize());
  215.     int h = fl_height();
  216.     if (h > hmax) hmax = h;
  217.   }
  218.   else {
  219.     // do each column separately as they may all set different fonts:
  220.     for (char* str = l->txt; *str; str++) {
  221.       Fl_Font font = textfont(); // default font
  222.       int size = textsize(); // default size
  223.       while (*str==format_char()) {
  224.     str++;
  225.     switch (*str++) {
  226.     case 'l': case 'L': size = 24; break;
  227.     case 'm': case 'M': size = 18; break;
  228.     case 's': size = 11; break;
  229.     case 'b': font = (Fl_Font)(font|FL_BOLD); break;
  230.     case 'i': font = (Fl_Font)(font|FL_ITALIC); break;
  231.     case 'f': case 't': font = FL_COURIER; break;
  232.     case 'S': size = strtol(str,&str,10); break;
  233.     case 'F': font = (Fl_Font)strtol(str,&str,10); break;
  234.     case 0: case '@': str--;
  235.     case '.': goto END_FORMAT;
  236.     }
  237.       }
  238.       END_FORMAT:
  239.       char* ptr = str;
  240.       for(;*str && (*str!=column_char()); str++) ;
  241.       if (ptr < str) {
  242.     fl_font(font, size); int h = fl_height();
  243.     if (h > hmax) hmax = h;
  244.       }
  245.       if (!*str) str --;
  246.     }
  247.   }
  248.  
  249.   return hmax; // previous version returned hmax+2!
  250. }
  251.  
  252. int Fl_Browser::item_width(void* v) const {
  253.   char* str = ((FL_BLINE*)v)->txt;
  254.   const int* i = column_widths();
  255.   int w = 0;
  256.  
  257.   while (*i) { // add up all tab-seperated fields
  258.     w += *i++;
  259.     char* e;
  260.     for (e = str; *e && *e != column_char(); e++);
  261.     if (!*e) return 0; // last one occupied by text
  262.     str = e+1;
  263.   }
  264.  
  265.   // OK, we gotta parse the string and find the string width...
  266.   int size = textsize();
  267.   Fl_Font font = textfont();
  268.   int done = 0;
  269.  
  270.   // MRS - might this cause problems on some platforms - order of operations?
  271.  
  272.   while (*str == format_char_ && *++str && *str != format_char_) {
  273.     switch (*str++) {
  274.     case 'l': case 'L': size = 24; break;
  275.     case 'm': case 'M': size = 18; break;
  276.     case 's': size = 11; break;
  277.     case 'b': font = (Fl_Font)(font|FL_BOLD); break;
  278.     case 'i': font = (Fl_Font)(font|FL_ITALIC); break;
  279.     case 'f': case 't': font = FL_COURIER; break;
  280.     case 'S':
  281.       size = strtol(str, &str, 10);
  282.       break;
  283.     case '.':
  284.       done = 1;
  285.     case '@':
  286.       str--;
  287.       done = 1;
  288.     }
  289.  
  290.     if (done)
  291.       break;
  292.   }
  293.  
  294.   fl_font(font, size);
  295.   return w + int(fl_width(str)) + 6;
  296. }
  297.  
  298. int Fl_Browser::full_height() const {
  299.   return full_height_;
  300. }
  301.  
  302. int Fl_Browser::incr_height() const {
  303.   return textsize()+2;
  304. }
  305.  
  306. void Fl_Browser::item_draw(void* v, int x, int y, int w, int h) const {
  307.   char* str = ((FL_BLINE*)v)->txt;
  308.   const int* i = column_widths();
  309.  
  310.   while (w > 6) {    // do each tab-seperated field
  311.     int w1 = w;    // width for this field
  312.     char* e = 0; // pointer to end of field or null if none
  313.     if (*i) { // find end of field and temporarily replace with 0
  314.       for (e = str; *e && *e != column_char(); e++);
  315.       if (*e) {*e = 0; w1 = *i++;} else e = 0;
  316.     }
  317.     int size = textsize();
  318.     Fl_Font font = textfont();
  319.     Fl_Color lcol = textcolor();
  320.     Fl_Align align = FL_ALIGN_LEFT;
  321.     // check for all the @-lines recognized by XForms:
  322.     while (*str == format_char() && *++str && *str != format_char()) {
  323.       switch (*str++) {
  324.       case 'l': case 'L': size = 24; break;
  325.       case 'm': case 'M': size = 18; break;
  326.       case 's': size = 11; break;
  327.       case 'b': font = (Fl_Font)(font|FL_BOLD); break;
  328.       case 'i': font = (Fl_Font)(font|FL_ITALIC); break;
  329.       case 'f': case 't': font = FL_COURIER; break;
  330.       case 'c': align = FL_ALIGN_CENTER; break;
  331.       case 'r': align = FL_ALIGN_RIGHT; break;
  332.       case 'B': 
  333.     fl_color((Fl_Color)strtol(str, &str, 10));
  334.     fl_rectf(x, y, w1, h);
  335.         break;
  336.       case 'C':
  337.     lcol = (Fl_Color)strtol(str, &str, 10);
  338.     break;
  339.       case 'F':
  340.     font = (Fl_Font)strtol(str, &str, 10);
  341.     break;
  342.       case 'N':
  343.     lcol = FL_INACTIVE_COLOR;
  344.     break;
  345.       case 'S':
  346.     size = strtol(str, &str, 10);
  347.     break;
  348.       case '-':
  349.     fl_color(FL_DARK3);
  350.     fl_line(x+3, y+h/2, x+w1-3, y+h/2);
  351.     fl_color(FL_LIGHT3);
  352.     fl_line(x+3, y+h/2+1, x+w1-3, y+h/2+1);
  353.     break;
  354.       case 'u':
  355.       case '_':
  356.     fl_color(lcol);
  357.     fl_line(x+3, y+h-1, x+w1-3, y+h-1);
  358.     break;
  359.       case '.':
  360.     goto BREAK;
  361.       case '@':
  362.     str--; goto BREAK;
  363.       }
  364.     }
  365.   BREAK:
  366.     fl_font(font, size);
  367.     if (!active_r()) lcol = inactive(lcol);
  368.     if (((FL_BLINE*)v)->flags & SELECTED)
  369.       lcol = contrast(lcol, selection_color());
  370.     fl_color(lcol);
  371.     fl_draw(str, x+3, y, w1-6, h, e ? Fl_Align(align|FL_ALIGN_CLIP) : align);
  372.     if (!e) break; // no more fields...
  373.     *e = column_char(); // put the seperator back
  374.     x += w1;
  375.     w -= w1;
  376.     str = e+1;
  377.   }
  378. }
  379.  
  380. static const int no_columns[1] = {0};
  381.  
  382. Fl_Browser::Fl_Browser(int x, int y, int w, int h, const char*l)
  383.   : Fl_Browser_(x, y, w, h, l) {
  384.   column_widths_ = no_columns;
  385.   lines = 0;
  386.   full_height_ = 0;
  387.   cacheline = 0;
  388.   format_char_ = '@';
  389.   column_char_ = '\t';
  390.   first = last = cache = 0;
  391. }
  392.  
  393. void Fl_Browser::lineposition(int line, Fl_Line_Position pos) {
  394.   if (line<1) line = 1;
  395.   if (line>lines) line = lines;
  396.   int p = 0;
  397.  
  398.   FL_BLINE* l;
  399.   for (l=first; l && line>1; l = l->next) {
  400.     line--; p += item_height(l);
  401.   }
  402.   if (l && (pos == BOTTOM)) p += item_height (l);
  403.  
  404.   int final = p, X, Y, W, H;
  405.   bbox(X, Y, W, H);
  406.  
  407.   switch(pos) {
  408.     case TOP: break;
  409.     case BOTTOM: final -= H; break;
  410.     case MIDDLE: final -= H/2; break;
  411.   }
  412.   
  413.   if (final > (full_height() - H)) final = full_height() -H;
  414.   position(final);
  415. }
  416.  
  417. int Fl_Browser::topline() const {
  418.   return lineno(top());
  419. }
  420.  
  421. void Fl_Browser::clear() {
  422.   for (FL_BLINE* l = first; l;) {
  423.     FL_BLINE* h = l->next;
  424.     free(l);
  425.     l = h;
  426.   }
  427.   full_height_ = 0;
  428.   first = 0;
  429.   lines = 0;
  430.   new_list();
  431. }
  432.  
  433. void Fl_Browser::add(const char* newtext, void* data) {
  434.   insert(lines+1, newtext, data);
  435.   //Fl_Browser_::display(last);
  436. }
  437.  
  438. const char* Fl_Browser::text(int line) const {
  439.   if (line < 1 || line > lines) return 0;
  440.   return find_line(line)->txt;
  441. }
  442.  
  443. void* Fl_Browser::data(int line) const {
  444.   if (line < 1 || line > lines) return 0;
  445.   return find_line(line)->data;
  446. }
  447.  
  448. int Fl_Browser::select(int line, int value) {
  449.   if (line < 1 || line > lines) return 0;
  450.   return Fl_Browser_::select(find_line(line), value);
  451. }
  452.  
  453. int Fl_Browser::selected(int line) const {
  454.   if (line < 1 || line > lines) return 0;
  455.   return find_line(line)->flags & SELECTED;
  456. }
  457.  
  458. void Fl_Browser::show(int line) {
  459.   FL_BLINE* t = find_line(line);
  460.   if (t->flags & NOTDISPLAYED) {
  461.     t->flags &= ~NOTDISPLAYED;
  462.     full_height_ += item_height(t);
  463.     if (Fl_Browser_::displayed(t)) redraw_lines();
  464.   }
  465. }
  466.  
  467. void Fl_Browser::hide(int line) {
  468.   FL_BLINE* t = find_line(line);
  469.   if (!(t->flags & NOTDISPLAYED)) {
  470.     full_height_ -= item_height(t);
  471.     t->flags |= NOTDISPLAYED;
  472.     if (Fl_Browser_::displayed(t)) redraw_lines();
  473.   }
  474. }
  475.  
  476. void Fl_Browser::display(int line, int value) {
  477.   if (line < 1 || line > lines) return;
  478.   if (value) show(line); else hide(line);
  479. }
  480.  
  481. int Fl_Browser::visible(int line) const {
  482.   if (line < 1 || line > lines) return 0;
  483.   return !(find_line(line)->flags&NOTDISPLAYED);
  484. }
  485.  
  486. int Fl_Browser::value() const {
  487.   return lineno(selection());
  488. }
  489.  
  490. //
  491. // End of "$Id: Fl_Browser.cxx,v 1.9.2.6 1999/05/11 09:39:29 bill Exp $".
  492. //
  493.